package org.renhui.network;

import android.content.Context;

import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.protocol.HTTP;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONTokener;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.SocketTimeoutException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * 服务器访问工具类
 */
public class HttpFetcher {
    private Context mAppContext;

    private final int mTimeoutRequest = 3 * 1000;   //链接请求超时时间
    private final int mTimeoutSO = 5 * 1000;        //Socket读写超时
    private final int mMaxRetry = 3;                //重试次数

    private String mUrlString;                      //要访问的地址
    private String mParas;                          //参数
    private HashMap<String, String> mJsonParams;    //Json格式的参数
    private HttpResponse mHttpResponse;             //服务器响应
    private HttpClient mHttpClient;
    private boolean mIsGet;                         //HTTP请求为Get类型的

    /**
     * 构造函数
     *
     * @param context 上下文环境
     * @param url     服务器地址
     * @param paras   参数
     * @param isGet   是不是GET方法
     */
    public HttpFetcher(Context context, String url, String paras, boolean isGet) {
        super();
        mAppContext = context.getApplicationContext();

        mUrlString = url;
        if (paras != null && paras.length() > 0) {
            mParas = paras;
        } else {
            mParas = null;
        }
        mJsonParams = new HashMap<>();
        mHttpResponse = null;
        mIsGet = isGet;
    }

    private boolean startGetFromServer() {
        HttpGet get;

        // 检查URL是否符合规范
        try {
            new UrlTranslator(mUrlString);
        } catch (MalformedURLException e) {
            return false;
        }

        if (mParas != null) {
            get = new HttpGet(mUrlString + "?" + mParas);
        } else {
            get = new HttpGet(mUrlString);
        }

        return startFetchFromServer(get);
    }

    private boolean startPostFromServer() {
        HttpPost post;

        try {
            new UrlTranslator(mUrlString);
        } catch (MalformedURLException e) {
            return false;
        }

        post = new HttpPost(mUrlString);
        List<NameValuePair> paras = new ArrayList<>();
        if (mParas != null) {
            String[] paraItem = mParas.split("&");

            for (String item : paraItem) {
                int position = item.indexOf("=");
                String paraName = item.substring(0, position);
                String paraValue = item.substring(position + 1);
                paras.add(new BasicNameValuePair(paraName, paraValue));
            }
        }

        if (mJsonParams.size() > 0) {
            for (String key : mJsonParams.keySet()) {
                paras.add(new BasicNameValuePair(key, mJsonParams.get(key)));
            }
        }

        try {
            if (paras.size() > 0) {
                post.setEntity(new UrlEncodedFormEntity(paras, HTTP.UTF_8));
            }
        } catch (UnsupportedEncodingException e) {
            DebugTraceTool.debugTraceException(this, e);
        }

        return startFetchFromServer(post);
    }


    private boolean startFetchFromServer(HttpUriRequest request) {
        boolean status = false;
        int retryCount = 0;

        if (request == null) {
            DebugTraceTool.debugTrace(this, "Get null request for: " + mUrlString + "?" + mParas);
            return false;
        }

        if (createHttpClient()) {
            while (mHttpResponse == null) {
                try {
                    mHttpResponse = mHttpClient.execute(request);
                } catch (ClientProtocolException e) {
                    DebugTraceTool.debugTraceException(this, e);
                    break;
                } catch (SocketTimeoutException e) {
                    DebugTraceTool.debugTraceException(this, e);
                    retryCount += 1;
                } catch (IOException e) {
                    DebugTraceTool.debugTraceException(this, e);
                    break;
                }

                if (retryCount >= mMaxRetry) {
                    break;
                }
            }

            // 有应答，且为200 ok
            // 需要针对此项功能进行测试
            if (mHttpResponse != null && (mHttpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK)) {
                DebugTraceTool.debugTrace(mHttpResponse.getStatusLine().getStatusCode());
                status = true;
                DebugTraceTool.debugTrace(this, "Response for " + mUrlString + "succeed.");
            } else {
                // 如果没有应答或者不是200 ok应答，则将mHttpResponse重新置空
                DebugTraceTool.debugTraceCritical(this, "request for " + mUrlString + " failed");
                mHttpResponse = null;
            }
        }
        return status;
    }

    public void syncFetch() {
        if (mIsGet) {
            startGetFromServer();
        } else {
            startPostFromServer();
        }
    }

    /**
     * 创建Http请求客户端
     *
     * @return boolean（创建是否成功）
     */
    private synchronized boolean createHttpClient() {
        if (mHttpResponse == null) {

            //设置连接超时时间和接受数据超时的时间
            BasicHttpParams params = new BasicHttpParams();
            HttpConnectionParams.setConnectionTimeout(params, mTimeoutRequest);
            HttpConnectionParams.setSoTimeout(params, mTimeoutSO);
            mHttpClient = new DefaultHttpClient(params);

            //设置UA参数（主要是给服务器端判断的时候使用的）
            //HttpProtocolParams.setUserAgent(mHttpClient.getParams(),"UA");
        }
        return true;
    }

    // 清理Session
    private void shutdownHttpClient() {
        if (mHttpClient != null) {
            ClientConnectionManager connectionManager = mHttpClient.getConnectionManager();

            if (connectionManager != null) {
                connectionManager.shutdown();
            }
            mHttpClient = null;
        }
    }
    
    public JSONObject getJsonResponse() {
        
        // 获取内容失败
        if (mHttpResponse == null) {
            shutdownHttpClient();
        }
        
        JSONObject object = null;
        try {
            String content = transStreamToString(mHttpResponse.getEntity().getContent());
            DebugTraceTool.debugTrace(this, "content" + content);
            
            if (content.length() > 0) {
                JSONTokener tokener = new JSONTokener(content);
                object = new JSONObject(tokener);
            } 
        } catch (IOException | JSONException e) {
            DebugTraceTool.debugTraceException(this, e);
        } finally {
            shutdownHttpClient();
        }
        return object;
    }

    /**
     * 将获取到的数据流转换成字符串 
     * @param input
     * @return
     */
    public String transStreamToString(InputStream input) {
        if (input == null) {
            return null;
        }

        BufferedReader reader = new BufferedReader(new InputStreamReader(input));
        StringBuilder sb = new StringBuilder();
        String line;
        try {
            while ((line = reader.readLine()) != null) {
                sb.append(line + "\n");
            }
        } catch (IOException e) {
            DebugTraceTool.debugTraceException(this, e);
        } finally {
            try {
                input.close();
            } catch (IOException e) {
                DebugTraceTool.debugTraceException(this, e);
            }
        }
        return sb.toString();
    }
    
}
